package org.hawk.gwt.ppc.parser.impl;

import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.lookupNext;
import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.readModifiers;
import static org.hawk.gwt.ppc.parser.impl.DescriptorParserUtils.readTypeName;

import java.util.ArrayList;
import java.util.List;

import org.hawk.gwt.ppc.parser.AnnotationDescriptor;
import org.hawk.gwt.ppc.parser.Descriptor;
import org.hawk.gwt.ppc.parser.DescriptorVisitor;
import org.hawk.gwt.ppc.parser.PreprocessorParseException;
import org.hawk.gwt.ppc.parser.Token;
import org.hawk.gwt.ppc.parser.VariableDescriptor;
import org.hawk.gwt.ppc.utils.PreprocessorException;

/**
 * Parses single variable, possibly annotated.
 * 
 * @author alex.bereznevatiy@gmail.com
 *
 */
final class VariableDescriptorParser implements DescriptorParser {

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#accepts(org.hawk.gwt.ppc.parser.Token)
	 */
	public boolean accepts(Token token) {
		return true;
	}

	/*
	 * (non-Javadoc)
	 * @see org.hawk.gwt.ppc.parser.impl.DescriptorParser#parse(org.hawk.gwt.ppc.parser.impl.TokenBasedJavaParser, org.hawk.gwt.ppc.parser.impl.Tokenizer, java.lang.Object[])
	 */
	public Descriptor parse(TokenBasedJavaParser parser, TokenSource tokenizer,
			Object... params) throws PreprocessorException {
		List<String> modifiers = new ArrayList<String>();
		Token first = tokenizer.current();
		readModifiers(modifiers, tokenizer);
		
		List<AnnotationDescriptor> annotations = new ArrayList<AnnotationDescriptor>(1);
		if (AnnotationDescriptorParser.parseAnnotations(parser, tokenizer, annotations)) {
			throw new PreprocessorParseException("Unexpected annotation type", tokenizer.current());
		}
		
		readModifiers(modifiers, tokenizer);
		StringBuilder type = new StringBuilder();
		readTypeName(type, tokenizer, true);
		
		boolean varArgs = false;
		if (tokenizer.current().equalsTo('.')) {
			lookupNext(tokenizer, false);
			if (!tokenizer.current().equalsTo('.')) {
				throw new PreprocessorParseException("Unknown token", tokenizer.current());
			}
			varArgs = true;
			lookupNext(tokenizer, false);
			if (tokenizer.current().equalsTo('.')) {
				lookupNext(tokenizer, false);
			}
		}
		
		String name = tokenizer.current().toString();
		Token last = tokenizer.current();
		lookupNext(tokenizer, true);
		
		return new VariableDescriptorImpl(first, last, name, type.toString(), modifiers, annotations, varArgs);
	}

	/**
	 * 
	 * @author alex.bereznevatiy@gmail.com
	 *
	 */
	final static class VariableDescriptorImpl extends AbstractExpressionDescriptor implements VariableDescriptor {
		
		private String name;
		private String type;
		private List<String> modifiers;
		private List<AnnotationDescriptor> annotations;
		private boolean isVarArgs;
		
		private VariableDescriptorImpl(Token first, Token last,
				String name, String type, List<String> modifiers,
				List<AnnotationDescriptor> annotations, boolean isVarArgs) {
			super(first, last);
			
			this.name = name;
			this.type = type;
			this.modifiers = unmodify(modifiers);
			this.annotations = unmodify(annotations);
			this.isVarArgs = isVarArgs;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.ModificatedDescriptor#getModifiers()
		 */
		public List<String> getModifiers() {
			return modifiers;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.AnnotatedDescriptor#getAnnotations()
		 */
		public List<AnnotationDescriptor> getAnnotations() {
			return annotations;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.VariableDescriptor#getName()
		 */
		public String getName() {
			return name;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.VariableDescriptor#getType()
		 */
		public String getType() {
			if (isVarArgs) {
				return type + "[]";
			}
			return type;
		}

		/**
		 * @return <code>true</code> if this variable represents variable arguments length parameter.
		 */
		boolean isVarArg() {
			return isVarArgs;
		}

		/*
		 * (non-Javadoc)
		 * @see org.hawk.gwt.ppc.parser.Descriptor#visit(org.hawk.gwt.ppc.parser.DescriptorVisitor)
		 */
		public void visit(DescriptorVisitor visitor) throws PreprocessorException {
			visitor.visit(this);
			for (Descriptor annotation : annotations) {
				annotation.visit(visitor);
			}
		}
	}
}
